package simco.framework.gui.services.data;

import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.osgi.framework.BundleContext;

import simco.framework.gui.utils.Configuration;
import simco.framework.models.CalendarContent;
import simco.framework.models.SimulationEvent;

/**
 * Tato trida reprezentuje datovy model kalendare
 * udalosti, ve kterem jsou uchovany udalosti
 * ulozene v kalendari udalosti jadra a take
 * udalosti, ktere jiz probehli.
 * 
 * Slouzi teze jako datovy model pro JList, do ktereho
 * jsou vypisovany udalosti kalendare.
 * 
 * @author Matej Prokop, mythge@students.zcu.cz
 */
public class CalendarModel extends AbstractListModel implements ListSelectionListener
{
	/**
	 * Seznam udalosti v kalendari
	 */
	private List<SimulationEventWrapper> eventsList;
	
	/**
	 * Index oznacene udalosti v kalendari
	 */
	private int selected;
	
	/**
	 * Aktualni simulacni cas v jadre
	 */
	private long currentTime;
	
	// OSGi bundle context injected by Spring DM
	/**
	 * OSGI budne context
	 */
	private BundleContext bundleContext;
	
	/**
	 * Konstanta indexu oznacene udalosti, definujici
	 * ze zadna udalost neni vybrana.
	 */
	public static final int NOT_SELECTED = -1;
	
	/**
	 * JList do ktereho je vykreslovan obsah kalendare
	 */
	private JList calendarList;
	
	/**
	 * Vytvori novy datovy model kalendare udalosti
	 */
	public CalendarModel()
	{
		this.eventsList = new ArrayList<SimulationEventWrapper>();
		this.selected = NOT_SELECTED;
		this.currentTime = -1;
	}
	
	/**
	 * Nastavuje OSGi bundle kontext
	 * @param bundleContext OSGi bundle kontext
	 */
	public void setBundleContext(BundleContext bundleContext) 
	{
		this.bundleContext = bundleContext;
	}
	
	/**
	 * Nastavuje JList, do ktereho jsou vykreslovany udalosti
	 * kalendare.
	 * @param calendarList komponenta, do ktereho jsou vypisovany
	 * udalosti kalendare.
	 */
	public void setCalendarList(JList calendarList) {
		this.calendarList = calendarList;
	}

	/**
	 * Odstrani vsechny zaznamy ulozene v datovem modelu.
	 */
	public synchronized void restartModel()
	{
		this.selected = NOT_SELECTED;
		this.currentTime = -1;
		
		if(!this.eventsList.isEmpty())
		{
			fireIntervalRemoved(this, 0, this.eventsList.size() - 1);			
			eventsList.clear();
		}		
	}
	
	/**
	 * Aktualizuje obsah vykreslovaneho kalendare udalosti,
	 * podle obsahuhu kalendare udalosti v jadre.
	 * @param content obsah kalendare udalosti jadra
	 */
	public synchronized void updateModel(CalendarContent content)
	{
		if(!this.eventsList.isEmpty())
		{
			fireIntervalRemoved(this, 0, this.eventsList.size() - 1);			
		}
		
		this.eventsList.clear();
		this.selected = NOT_SELECTED;
		
		this.currentTime = content.getCurrentTime();
		
		for(SimulationEvent se : content.getEvents())
		{
			String sourceName =  bundleContext.getBundle(se.getSource()).getSymbolicName();
			String targetName =  bundleContext.getBundle(se.getTarget()).getSymbolicName();
			eventsList.add(new SimulationEventWrapper(se.getTime(), se,
					sourceName, targetName));
		}
		
		if(this.eventsList.isEmpty())
		{
			return;
		}			
		
		fireIntervalAdded(this, 0, this.eventsList.size() - 1);
		
		SwingUtilities.invokeLater(new Runnable()
		{
			public void run() 
			{
				calendarList.ensureIndexIsVisible(getSize() - 1);				
			}			
		});
	}
	
	/**
	 * Vrati aktualne vybranou udalosti v kalendari
	 * @return vrati aktualne vybranou udalost v
	 * kalendari nebo null, pokud zadna udalost
	 * neni vybrana
	 */
	public SimulationEvent getSelectedEvent()
	{
		if(selected == NOT_SELECTED)
		{
			return null;
		}
		
		return eventsList.get(selected).getEvent();
	}

	@Override
	public Object getElementAt(int index)
	{
		return eventsList.get(index);
	}

	@Override
	public synchronized int getSize() 
	{		
		return this.eventsList.size();
	}
	
	@Override
	public void valueChanged(ListSelectionEvent e) 
	{
		if(!e.getValueIsAdjusting())
		{
			JList list = (JList) e.getSource();
			
			if(list.isSelectionEmpty())
			{
				selected = NOT_SELECTED;
			}
			else
			{
				selected = list.getSelectedIndex();				
			}			
		}
		
	}

	/**
	 * Nastavi vybrany index
	 * @param index vybrany index
	 */
	public void setSelection(int index)
	{
		this.selected = index;
	}
	
	/**
	 * Vraci logickou hodnotu, jestli je vybrana
	 * nejaka udalost v kalendari
	 * @return true pokud je v kalendari vybrana (oznacena)
	 * nejaka udalost
	 */
	public boolean isSelected()
	{
		return (selected != NOT_SELECTED);
	}

	/**
	 * Vraci novou instanci cell rendereru, ktery zvyraznuje
	 * udalosti pro aktulani simulacni cas, preddefinovanou
	 * barvou.
	 * 
	 * @return cell renderer
	 */
	public ListCellRenderer getCellRenderer()
	{
		return new MyCellRenderer();
	}

	/**
	 * Tato trida implementuje cellrenderer, ktery zvyraznuje
	 * udalosti pro aktulani simulacni cas, preddefinovanou
	 * barvou.
	 * 
	 * @author Matej Prokop, mythge@students.zcu.cz
	 *
	 */
	class MyCellRenderer extends JLabel implements ListCellRenderer 
	{
		/**
		 * Vraci novou instaci cell rendereru
		 */
	     public MyCellRenderer() 
	     {
	         setOpaque(true);
	     }

	     /**
	      * Vraci JLabel, pro kazdy radek kalendar a aplikuje
	      * zvyrazneni nekterych udalosti.
	      */
	     public Component getListCellRendererComponent(JList list,
	                                                   Object value,
	                                                   int index,
	                                                   boolean isSelected,
	                                                   boolean cellHasFocus) 
	     {
	         setText(value.toString());

	         Color background;
	         Color foreground;

	         JList.DropLocation dropLocation = list.getDropLocation();
	         if (dropLocation != null
	                 && !dropLocation.isInsert()
	                 && dropLocation.getIndex() == index) {

	             background = Color.BLUE;
	             foreground = Color.WHITE;

	         } 
	         else if (isSelected) 
	         {
	        	 // vybrana udalost v kalendari:
	             background = Configuration.getColorOfSelected();
	             foreground = Color.BLACK;
	         }
	         else if(((SimulationEventWrapper) value).getEventTime() == currentTime)
	         {
	        	 // udalost pro aktualni simulacni cas
	        	 background = Configuration.getColorOfMarked();
	             foreground = Color.WHITE;
	         }
	         else 
	         {
	        	 // vsechny ostatni udalosti:	        	 
	             background = Color.WHITE;
	             foreground = Color.BLACK;
	         };

	         setBackground(background);
	         setForeground(foreground);
	         
	         return this;
	     }
	 }
	 
	 
}
